#include <sqlite3.h>
#include <stdio.h>

/*
	功能：
			在这个程序中，我们从当前工作目录中读取一个图像，
			并将其写入SQLite数据库的表中。

			在本节中，我们将向SQLite数据库插入一个图像。请注意，有些人反对将图像放入数据库。
			在这里，我们只展示如何做到这一点。我们不会纠缠于是否将图像保存在数据库中的技术问题。

			sqlite> CREATE TABLE Images(Id INTEGER PRIMARY KEY, Data BLOB);
			对于此示例，我们创建一个名为 Images 的新表。
			对于图像，我们使用数据类型，它代表二进制大对象。BLOB

			gcc -o insert_image insert_image.c -lsqlite3
			./insert_image
*/


int main(int argc, char **argv) {

	/*
		我们从文件系统中读取二进制数据。
		我们有一个名为woman.jpg的 JPG 图像。
		该函数打开指定的文件进行读取。
		如果操作失败，它将返回指向 FILE 对象的指针或空值。
		woman.jpgfopen()
	*/

    FILE *fp = fopen("./images/woman.jpg", "rb");
    
    if (fp == NULL) {
        
        fprintf(stderr, "Cannot open image file\n");    
        
        return 1;
    }

	/*
		我们使用该函数将文件指针移动到文件的末尾。
		我们需要确定图像的大小。
		如果发生错误，则设置错误指示器。
		我们使用该函数检查指标。
		如果出现错误，将关闭打开的文件处理程序。
		fseek()fseek()
	*/
    
    fseek(fp, 0, SEEK_END);
    
    if (ferror(fp)) {
        
        fprintf(stderr, "fseek() failed\n");
        int r = fclose(fp);

        if (r == EOF) {
            fprintf(stderr, "Cannot close file handler\n");          
        }    
        
        return 1;
    }  

	/*	
		对于二进制流，该函数返回从文件开头开始的字节数，
		例如图像文件的大小。
		如果出现错误，该函数返回 -1 并设置 。
		该函数将 errno 的值解释为错误消息，并将其打印到标准错误输出流。
		ftell()errnoperror()
	*/
    
    int flen = ftell(fp);
    
    if (flen == -1) {
        
        perror("error occurred");
        int r = fclose(fp);

        if (r == EOF) {
            fprintf(stderr, "Cannot close file handler\n");
        }
        
        return 1;     
    }

    //我们使用该函数将文件指针移动到文件的当前位置。
    fseek(fp, 0, SEEK_SET);
    
    if (ferror(fp)) {
        
        fprintf(stderr, "fseek() failed\n");
        int r = fclose(fp);

        if (r == EOF) {
            fprintf(stderr, "Cannot close file handler\n");
        }    
        
        return 1;
    }

	//此数组将存储图像数据。
    char data[flen+1];

	//该函数从文件指针读取数据并将其存储在数据数组中。该函数返回成功读取的元素数。fread()
    int size = fread(data, 1, flen, fp);
    
    if (ferror(fp)) {
        
        fprintf(stderr, "fread() failed\n");
        int r = fclose(fp);

        if (r == EOF) {
            fprintf(stderr, "Cannot close file handler\n");
        }
        
        return 1;     
    }
    //读取数据后，我们可以关闭文件处理程序。
    int r = fclose(fp);

    if (r == EOF) {
        fprintf(stderr, "Cannot close file handler\n");
    }    
 
  
    sqlite3 *db;
    char *err_msg = 0;
    
    int rc = sqlite3_open("test.db", &db);
    
    if (rc != SQLITE_OK) {
        
        fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        
        return 1;
    }
    
    
    sqlite3_stmt *pStmt;

	//此 SQL 语句用于将图像插入到数据库中。
    char *sql = "INSERT INTO Images(Data) VALUES(?)";

    //将编译 SQL 语句。
    rc = sqlite3_prepare(db, sql, -1, &pStmt, 0);
    
    if (rc != SQLITE_OK) {
        
        fprintf(stderr, "Cannot prepare statement: %s\n", sqlite3_errmsg(db));
        
        return 1;
    }    

    /*
		该函数将二进制数据绑定到已编译的语句。
		该参数表示指向内容信息的指针是静态的，不需要释放。
		sqlite3_bind_blob()SQLITE_STATIC
    */
    sqlite3_bind_blob(pStmt, 1, data, size, SQLITE_STATIC);    

    //执行该语句并将图像写入表。
    rc = sqlite3_step(pStmt);
    
    if (rc != SQLITE_DONE) {
        
        printf("execution failed: %s", sqlite3_errmsg(db));
    }
        
    sqlite3_finalize(pStmt);    

    sqlite3_close(db);

    return 0;
}

